package com.elvish.efast.common.utils;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;

/**
 * 树工具类
 *
 * @author elvish
 * @date 2018/11/2 13:40
 * @blog oschina(https : / / my.oschina.net / xiaozhutefannao)
 */
public class TreeUtil {

    /**
     * 构建树形结构
     *
     * @param list    数据集合
     * @param pidName 父类标识字段
     * @return 树形结构集合
     */
    public static <T> List<T> buildTree(List<T> list, String pidName) {

        Map<String, T> temp = Maps.newHashMap();
        for (T obj : list) {
            temp.put(getObjFieldValue(obj, "id").toString(), obj);
        }

        return buildTree(temp, list, pidName);
    }

    private static <T> List<T> buildTree(Map<String, T> temp, List<T> list, String pidName) {
        List<T> rootMenus = Lists.newArrayList();
        for (T obj : list) {
            // 获取一级节点数据
            T parent = temp.get(getObjFieldValue(obj, pidName));
            if (parent == null) {
                // 添加一级节点数据
                rootMenus.add(obj);
            } else {
                buildChildren(parent, obj);
            }

        }
        return rootMenus;
    }

    private static <T> void buildChildren(T parent, T obj) {
        // 二级及二级以下节点
        // parent为二级
        List<T> children = (List<T>) getObjFieldValue(parent, "children");
        if (children == null) {
            children = Lists.newArrayList();
            setObjFieldValue(parent, "setChildren", children);
        }
        children.add(obj);
    }


    /**
     * 调用模型方法
     *
     * @param obj        模型
     * @param methodName 方法
     * @param list       数据集合
     */
    private static <T> void setObjFieldValue(T obj, String methodName, List<T> list) {
        Class<?> clz = obj.getClass();
        Method method;
        try {
            method = clz.getDeclaredMethod(methodName, List.class);
            if (method.isAccessible()) {
                method.setAccessible(true);
            }
            method.invoke(obj, list);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }


    /**
     * 获取模型属性值
     *
     * @param obj       模型
     * @param fieldName 属性名称
     * @return 模型属性值
     */
    private static <T> T getObjFieldValue(Object obj, String fieldName) {
        Class<?> clz = obj.getClass();
        Field field;
        try {
            field = clz.getDeclaredField(fieldName);
            if (!field.isAccessible()) {
                field.setAccessible(true);
            }
            return (T) field.get(obj);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (NumberFormatException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }


}
